-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(replay): Capture request/response body size for xhr/fetch breadcrumbs #7407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
function _xhrBreadcrumb(handlerData: HandlerData & { xhr: SentryWrappedXMLHttpRequest }): void { | ||
if (handlerData.endTimestamp) { | ||
// We only capture complete, non-sentry requests | ||
if (handlerData.xhr.__sentry_own_request__) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually checked in the "original" breadcrumb integration in browser already, so no need to check this here again IMHO:
https://github.com/getsentry/sentry-javascript/blob/develop/packages/browser/src/integrations/breadcrumbs.ts#L222
Actually the same is also true for the endTimestamp/sentry_xhr fields, but there it makes more sense to guard them defensively again, I'd say.
|
||
if (!handlerData.endTimestamp) { | ||
if (!startTimestamp || !endTimestamp || !xhr.__sentry_xhr__) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not 100% sure, but I don't think the "moving timestamps around & checking differnent places" stuff is really needed here (anymore), this should always be set, and IMHO if one of these fails we just bail out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, let's try it this way.
const onreadystatechangeHandler = function (): void { | ||
if (xhr.readyState === 4) { | ||
const onreadystatechangeHandler: () => void = () => { | ||
// For whatever reason, this is not the same instance here as from the outer method |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is extremely weird and I wasted a lot of time figuring out why this is happening, to no avail.
basically, it seems that when we just assign to xhrInfo
from the outer scope, this is not the same as this.sentry_xhr, even though the this
remains the same, and I don't see any other place where we'd set this 😬
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooof no idea.. but given that integration tests are passing, I'd say let's just go with it...
size-limit report 📦
|
I will investigate if we can make this opt-in with less bundle size impact, let's see... |
37124b1
to
afa7415
Compare
So I refactored this for a smaller bundle size impact, into a new optional integration. I updated the OP accordingly, IMHO this is also something we can then later reuse for adding e.g. the whole request/response body, if we ever need/want this. |
request_body_size
& response_body_size
to fetch/xhrrequest_body_size
& response_body_size
to fetch/xhr
request_body_size
& response_body_size
to fetch/xhrExtendedNetworkBreadcrumbs
integration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Had some minor comments but I like the refactor of the types and the idea with the optional integration. I think it's justified to tell replay users to use this integration if they want to monitor their request bodies (which I know we're not doing yet but eventually).
packages/types/src/client.ts
Outdated
/** | ||
* Register a callback before a breadcrumb is added. | ||
*/ | ||
on?(hook: 'beforeBreadcrumb', callback: (breadcrumb: Breadcrumb, hint?: BreadcrumbHint) => void): void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
l: Hmm we already have a top-level callback called beforeBreadcrumb
. my overall logaf is low here but do you think we should rename the hook to something like beforeAddBreadcrumb
or something similar?
I'd just like to avoid confusion around the callback and the hook if that makes sense. Although, if we're moving into a hooks world, we might eventually even drop the top level callbacks or refactor them to use the hooks internally. So IMO we can also leave it as is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I had the same thought. I guess beforeAddBreadcrumb
makes sense 👍
import { sentryTest } from '../../../../../utils/fixtures'; | ||
import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; | ||
|
||
sentryTest('adds a breadcrumb for basic fetch GET request', async ({ getLocalTestPath, page }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
l: So here we're basically testing that the response_body_size
is set based on the body size rather than the non-existing content-length
header, right? Then let's reflect this in the test title.
I know we currently don't have integration tests for the normal breadcrumbs integration. I think we actually might want to add some for it because having a test with what the title suggests is still valuable IMO. Doesn't have to happen in this PR of course!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense! 👍
import { sentryTest } from '../../../../../utils/fixtures'; | ||
import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; | ||
|
||
sentryTest('adds a breadcrumb for basic XHR GET request', async ({ getLocalTestPath, page }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
l: Same thing here as for the equivalent fetch test
|
||
if (!handlerData.endTimestamp) { | ||
if (!startTimestamp || !endTimestamp || !xhr.__sentry_xhr__) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, let's try it this way.
const onreadystatechangeHandler = function (): void { | ||
if (xhr.readyState === 4) { | ||
const onreadystatechangeHandler: () => void = () => { | ||
// For whatever reason, this is not the same instance here as from the outer method |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooof no idea.. but given that integration tests are passing, I'd say let's just go with it...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way we could automatically opt in Replay users to this integration? I think this is a pretty big barrier for what we should consider "basic" functionality for Replay.
efb115c
to
8d07e33
Compare
Yes, I also think it is a good separation of concerns to say "If you want any extended network monitoring, just use this integration (which can have settings in the future)", and keep the default experience as slim as possible :) |
db663d1
to
0916dc3
Compare
ExtendedNetworkBreadcrumbs
integration
I updated this to be part of replay, it will be automatically applied there. |
@@ -4,5 +4,5 @@ | |||
{ | |||
"extends": "../tsconfig.test.json", | |||
|
|||
"include": ["./**/*"] | |||
"include": ["./**/*", "../../replay/test/unit/coreHandlers/extendednetworkbreadcrumbs.test.ts"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this case sensitive?
913064d
to
c63f22e
Compare
PR feedback rename tests & add breadcrumb tests browser integraiton tests move functionality to replay text encoder ensure we visit url avoid page goto? un-flake XHR/fetch tests revert node unit test changes?? improve replay test try make less brittle? skip xhr tests in non-chromium fix test
c63f22e
to
9db62c7
Compare
This ensures that in Replay we capture more details for xhr/fetch breadcrumbs.
It will enrich the breadcrumbs with
request_body_size
andresponse_body_size
fields.This works as follows:
Content-Length
header, if available. No further processing needed!For replay, we pass these through as
requestBodySize
andresponseBodySize
- we use camelCase there 😝For regular breadcrumbs, this looks as follow in the current UI:
I refined types a bit to make this easier, we had a few places where we were still using incomplete types (and for replay, duplicating the types sometimes). This should now be reasonably type safe.
Caveats / notes
fetch
we do not get the response body size if the Content-Length header is not set. The reason for this is that this is async in fetch - we'd need to do:Closes #7373